unit MessageTracer;

interface

uses
  Classes,
  Windows,
  Messages,
  SysUtils,
  Controls;

type
  TTraceEvent = procedure(const aMessage: string) of object;
  TMessageTracer = class(TComponent)
  strict private
    fTarget: TControl;
    fOnTrace: TTraceEvent;
    fOriginalWndProc: TWndMethod;
    fIgnores: TList;
    procedure Trace(const aMessage: string); inline;
    procedure Catch(const aTarget: TControl); inline;
    procedure UnCatch(const aTarget: TControl); inline;
    procedure HookWndProc(var Message: TMessage);
    function GetMessageInfoString(const m: TMessage): string;
  protected
    procedure DoTrace(const aMessage: string); virtual;
    procedure DoCatch(const aTarget: TControl); virtual;
    procedure DoUnCatch(const aTarget: TControl); virtual;
  published
  public
    constructor Create(AOwner: TComponent); overload; override;  
    constructor Create(const aOwner: TComponent; const aTarget: TControl); overload; 
    destructor Destroy; override;
    procedure Ignore(const aMsgs: array of Integer);
    procedure UnIgnore(const aMsgs: array of Integer);
    procedure Start;
    procedure Stop;
  published
    property OnTrace: TTraceEvent read fOnTrace write fOnTrace;
  end;

  TDebugMessageTracer = class(TMessageTracer)
  protected
    procedure DoTrace(const aMessage: string); override;
  end;

  TConsoleMessageTracer = class(TMessageTracer)
  strict private
    class var Initialized: Boolean;
    class procedure Initialize; static;
  protected
    procedure DoTrace(const aMessage: string); override;
    procedure DoCatch(const aTarget: TControl); override;
  public
    destructor Destroy; override;
  end;

procedure Register;

implementation

procedure Register;
begin
  RegisterComponents('LEX', [TMessageTracer, TDebugMessageTracer, TConsoleMessageTracer]);
end;

{ TMessageTracer }

constructor TMessageTracer.Create(AOwner: TComponent);
begin
  inherited;
  fIgnores := TList.Create;
end;

procedure TMessageTracer.Catch(const aTarget: TControl);
begin
  fOriginalWndProc := aTarget.WindowProc;
  aTarget.WindowProc := HookWndProc;
  DoCatch(aTarget);
end;

procedure TMessageTracer.UnCatch(const aTarget: TControl);
begin
  if not Assigned(fOriginalWndProc) then
    raise EInvalidOperation.Create('OriginalWndProc cannot be nil');

  DoUnCatch(aTarget);
  fTarget.WindowProc := fOriginalWndProc;
  fOriginalWndProc := nil;
end;

procedure TMessageTracer.UnIgnore(const aMsgs: array of Integer);
var
  m: Integer;
begin
  for m in aMsgs do
    fIgnores.Remove(Pointer(m));
end;

constructor TMessageTracer.Create(const aOwner: TComponent; const aTarget: TControl);
begin
  Assert(Assigned(aTarget), 'Target must be specified');

  Create(aOwner);
  fTarget := aTarget;
end;

destructor TMessageTracer.Destroy;
begin
  if Assigned(fOriginalWndProc) then
    UnCatch(fTarget);
  FreeAndNil(fIgnores);
  inherited;
end;

procedure TMessageTracer.DoCatch(const aTarget: TControl);
begin
end;

procedure TMessageTracer.DoUnCatch(const aTarget: TControl);
begin
end;

function TMessageTracer.GetMessageInfoString(const m: TMessage): string;
begin
  case m.Msg of
//  WINDOWS MESSAGES  
    WM_NULL: Result :=  'WM_NULL';
    WM_CREATE: Result :=  'WM_CREATE';
    WM_DESTROY: Result :=  'WM_DESTROY';
    WM_MOVE: Result :=  'WM_MOVE';
    WM_SIZE: Result :=  'WM_SIZE';
    WM_ACTIVATE: Result :=  'WM_ACTIVATE';
    WM_SETFOCUS: Result :=  'WM_SETFOCUS';
    WM_KILLFOCUS: Result :=  'WM_KILLFOCUS';
    WM_ENABLE: Result :=  'WM_ENABLE';
    WM_SETREDRAW: Result :=  'WM_SETREDRAW';
    WM_SETTEXT: Result :=  'WM_SETTEXT';
    WM_GETTEXT: Result :=  'WM_GETTEXT';
    WM_GETTEXTLENGTH: Result :=  'WM_GETTEXTLENGTH';
    WM_PAINT: Result :=  'WM_PAINT';
    WM_CLOSE: Result :=  'WM_CLOSE';
    WM_QUERYENDSESSION: Result :=  'WM_QUERYENDSESSION';
    WM_QUIT: Result :=  'WM_QUIT';
    WM_QUERYOPEN: Result :=  'WM_QUERYOPEN';
    WM_ERASEBKGND: Result :=  'WM_ERASEBKGND';
    WM_SYSCOLORCHANGE: Result :=  'WM_SYSCOLORCHANGE';
    WM_ENDSESSION: Result :=  'WM_ENDSESSION';
    WM_SYSTEMERROR: Result :=  'WM_SYSTEMERROR';
    WM_SHOWWINDOW: Result :=  'WM_SHOWWINDOW';
    WM_CTLCOLOR: Result :=  'WM_CTLCOLOR';
    WM_WININICHANGE: Result :=  'WM_WININICHANGE';
    WM_DEVMODECHANGE: Result :=  'WM_DEVMODECHANGE';
    WM_ACTIVATEAPP: Result :=  'WM_ACTIVATEAPP';
    WM_FONTCHANGE: Result :=  'WM_FONTCHANGE';
    WM_TIMECHANGE: Result :=  'WM_TIMECHANGE';
    WM_CANCELMODE: Result :=  'WM_CANCELMODE';
    WM_SETCURSOR: Result :=  'WM_SETCURSOR';
    WM_MOUSEACTIVATE: Result :=  'WM_MOUSEACTIVATE';
    WM_CHILDACTIVATE: Result :=  'WM_CHILDACTIVATE';
    WM_QUEUESYNC: Result :=  'WM_QUEUESYNC';
    WM_GETMINMAXINFO: Result :=  'WM_GETMINMAXINFO';
    WM_PAINTICON: Result :=  'WM_PAINTICON';
    WM_ICONERASEBKGND: Result :=  'WM_ICONERASEBKGND';
    WM_NEXTDLGCTL: Result :=  'WM_NEXTDLGCTL';
    WM_SPOOLERSTATUS: Result :=  'WM_SPOOLERSTATUS';
    WM_DRAWITEM: Result :=  'WM_DRAWITEM';
    WM_MEASUREITEM: Result :=  'WM_MEASUREITEM';
    WM_DELETEITEM: Result :=  'WM_DELETEITEM';
    WM_VKEYTOITEM: Result :=  'WM_VKEYTOITEM';
    WM_CHARTOITEM: Result :=  'WM_CHARTOITEM';
    WM_SETFONT: Result :=  'WM_SETFONT';
    WM_GETFONT: Result :=  'WM_GETFONT';
    WM_SETHOTKEY: Result :=  'WM_SETHOTKEY';
    WM_GETHOTKEY: Result :=  'WM_GETHOTKEY';
    WM_QUERYDRAGICON: Result :=  'WM_QUERYDRAGICON';
    WM_COMPAREITEM: Result :=  'WM_COMPAREITEM';
    WM_GETOBJECT: Result :=  'WM_GETOBJECT';
    WM_COMPACTING: Result :=  'WM_COMPACTING';
    WM_COMMNOTIFY: Result :=  'WM_COMMNOTIFY';
    WM_WINDOWPOSCHANGING: Result :=  'WM_WINDOWPOSCHANGING';
    WM_WINDOWPOSCHANGED: Result :=  'WM_WINDOWPOSCHANGED';
    WM_POWER: Result :=  'WM_POWER';
    WM_COPYDATA: Result :=  'WM_COPYDATA';
    WM_CANCELJOURNAL: Result :=  'WM_CANCELJOURNAL';
    WM_NOTIFY: Result :=  'WM_NOTIFY';
    WM_INPUTLANGCHANGEREQUEST: Result :=  'WM_INPUTLANGCHANGEREQUEST';
    WM_INPUTLANGCHANGE: Result :=  'WM_INPUTLANGCHANGE';
    WM_TCARD: Result :=  'WM_TCARD';
    WM_HELP: Result :=  'WM_HELP';
    WM_USERCHANGED: Result :=  'WM_USERCHANGED';
    WM_NOTIFYFORMAT: Result :=  'WM_NOTIFYFORMAT';
    WM_CONTEXTMENU: Result :=  'WM_CONTEXTMENU';
    WM_STYLECHANGING: Result :=  'WM_STYLECHANGING';
    WM_STYLECHANGED: Result :=  'WM_STYLECHANGED';
    WM_DISPLAYCHANGE: Result :=  'WM_DISPLAYCHANGE';
    WM_GETICON: Result :=  'WM_GETICON';
    WM_SETICON: Result :=  'WM_SETICON';
    WM_NCCREATE: Result :=  'WM_NCCREATE';
    WM_NCDESTROY: Result :=  'WM_NCDESTROY';
    WM_NCCALCSIZE: Result :=  'WM_NCCALCSIZE';
    WM_NCHITTEST: Result :=  'WM_NCHITTEST';
    WM_NCPAINT: Result :=  'WM_NCPAINT';
    WM_NCACTIVATE: Result :=  'WM_NCACTIVATE';
    WM_GETDLGCODE: Result :=  'WM_GETDLGCODE';
    WM_NCMOUSEMOVE: Result :=  'WM_NCMOUSEMOVE';
    WM_NCLBUTTONDOWN: Result :=  'WM_NCLBUTTONDOWN';
    WM_NCLBUTTONUP: Result :=  'WM_NCLBUTTONUP';
    WM_NCLBUTTONDBLCLK: Result :=  'WM_NCLBUTTONDBLCLK';
    WM_NCRBUTTONDOWN: Result :=  'WM_NCRBUTTONDOWN';
    WM_NCRBUTTONUP: Result :=  'WM_NCRBUTTONUP';
    WM_NCRBUTTONDBLCLK: Result :=  'WM_NCRBUTTONDBLCLK';
    WM_NCMBUTTONDOWN: Result :=  'WM_NCMBUTTONDOWN';
    WM_NCMBUTTONUP: Result :=  'WM_NCMBUTTONUP';
    WM_NCMBUTTONDBLCLK: Result :=  'WM_NCMBUTTONDBLCLK';
    WM_NCXBUTTONDOWN: Result :=  'WM_NCXBUTTONDOWN';
    WM_NCXBUTTONUP: Result :=  'WM_NCXBUTTONUP';
    WM_NCXBUTTONDBLCLK: Result :=  'WM_NCXBUTTONDBLCLK';
    WM_INPUT: Result :=  'WM_INPUT';
    WM_KEYFIRST: Result :=  'WM_KEYFIRST';
    WM_KEYUP: Result :=  'WM_KEYUP';
    WM_CHAR: Result :=  'WM_CHAR';
    WM_DEADCHAR: Result :=  'WM_DEADCHAR';
    WM_SYSKEYDOWN: Result :=  'WM_SYSKEYDOWN';
    WM_SYSKEYUP: Result :=  'WM_SYSKEYUP';
    WM_SYSCHAR: Result :=  'WM_SYSCHAR';
    WM_SYSDEADCHAR: Result :=  'WM_SYSDEADCHAR';
    WM_KEYLAST: Result :=  'WM_KEYLAST';
    WM_INITDIALOG: Result :=  'WM_INITDIALOG';
    WM_COMMAND: Result :=  'WM_COMMAND';
    WM_SYSCOMMAND: Result :=  'WM_SYSCOMMAND';
    WM_TIMER: Result :=  'WM_TIMER';
    WM_HSCROLL: Result :=  'WM_HSCROLL';
    WM_VSCROLL: Result :=  'WM_VSCROLL';
    WM_INITMENU: Result :=  'WM_INITMENU';
    WM_INITMENUPOPUP: Result :=  'WM_INITMENUPOPUP';
    WM_MENUSELECT: Result :=  'WM_MENUSELECT';
    WM_MENUCHAR: Result :=  'WM_MENUCHAR';
    WM_ENTERIDLE: Result :=  'WM_ENTERIDLE';
    WM_MENURBUTTONUP: Result :=  'WM_MENURBUTTONUP';
    WM_MENUDRAG: Result :=  'WM_MENUDRAG';
    WM_MENUGETOBJECT: Result :=  'WM_MENUGETOBJECT';
    WM_UNINITMENUPOPUP: Result :=  'WM_UNINITMENUPOPUP';
    WM_MENUCOMMAND: Result :=  'WM_MENUCOMMAND';
    WM_CHANGEUISTATE: Result :=  'WM_CHANGEUISTATE';
    WM_UPDATEUISTATE: Result :=  'WM_UPDATEUISTATE';
    WM_QUERYUISTATE: Result :=  'WM_QUERYUISTATE';
    WM_CTLCOLORMSGBOX: Result :=  'WM_CTLCOLORMSGBOX';
    WM_CTLCOLOREDIT: Result :=  'WM_CTLCOLOREDIT';
    WM_CTLCOLORLISTBOX: Result :=  'WM_CTLCOLORLISTBOX';
    WM_CTLCOLORBTN: Result :=  'WM_CTLCOLORBTN';
    WM_CTLCOLORDLG: Result :=  'WM_CTLCOLORDLG';
    WM_CTLCOLORSCROLLBAR: Result :=  'WM_CTLCOLORSCROLLBAR';
    WM_CTLCOLORSTATIC: Result :=  'WM_CTLCOLORSTATIC';
    WM_MOUSEFIRST: Result :=  'WM_MOUSEFIRST';
    WM_LBUTTONDOWN: Result :=  'WM_LBUTTONDOWN';
    WM_LBUTTONUP: Result :=  'WM_LBUTTONUP';
    WM_LBUTTONDBLCLK: Result :=  'WM_LBUTTONDBLCLK';
    WM_RBUTTONDOWN: Result :=  'WM_RBUTTONDOWN';
    WM_RBUTTONUP: Result :=  'WM_RBUTTONUP';
    WM_RBUTTONDBLCLK: Result :=  'WM_RBUTTONDBLCLK';
    WM_MBUTTONDOWN: Result :=  'WM_MBUTTONDOWN';
    WM_MBUTTONUP: Result :=  'WM_MBUTTONUP';
    WM_MBUTTONDBLCLK: Result :=  'WM_MBUTTONDBLCLK';
    WM_MOUSEWHEEL: Result :=  'WM_MOUSEWHEEL';
    WM_PARENTNOTIFY: Result :=  'WM_PARENTNOTIFY';
    WM_ENTERMENULOOP: Result :=  'WM_ENTERMENULOOP';
    WM_EXITMENULOOP: Result :=  'WM_EXITMENULOOP';
    WM_NEXTMENU: Result :=  'WM_NEXTMENU';
    WM_SIZING: Result :=  'WM_SIZING';
    WM_CAPTURECHANGED: Result :=  'WM_CAPTURECHANGED';
    WM_MOVING: Result :=  'WM_MOVING';
    WM_POWERBROADCAST: Result :=  'WM_POWERBROADCAST';
    WM_DEVICECHANGE: Result :=  'WM_DEVICECHANGE';
    WM_IME_STARTCOMPOSITION: Result :=  'WM_IME_STARTCOMPOSITION';
    WM_IME_ENDCOMPOSITION: Result :=  'WM_IME_ENDCOMPOSITION';
    WM_IME_COMPOSITION: Result :=  'WM_IME_COMPOSITION';
    WM_IME_SETCONTEXT: Result :=  'WM_IME_SETCONTEXT';
    WM_IME_NOTIFY: Result :=  'WM_IME_NOTIFY';
    WM_IME_CONTROL: Result :=  'WM_IME_CONTROL';
    WM_IME_COMPOSITIONFULL: Result :=  'WM_IME_COMPOSITIONFULL';
    WM_IME_SELECT: Result :=  'WM_IME_SELECT';
    WM_IME_CHAR: Result :=  'WM_IME_CHAR';
    WM_IME_REQUEST: Result :=  'WM_IME_REQUEST';
    WM_IME_KEYDOWN: Result :=  'WM_IME_KEYDOWN';
    WM_IME_KEYUP: Result :=  'WM_IME_KEYUP';
    WM_MDICREATE: Result :=  'WM_MDICREATE';
    WM_MDIDESTROY: Result :=  'WM_MDIDESTROY';
    WM_MDIACTIVATE: Result :=  'WM_MDIACTIVATE';
    WM_MDIRESTORE: Result :=  'WM_MDIRESTORE';
    WM_MDINEXT: Result :=  'WM_MDINEXT';
    WM_MDIMAXIMIZE: Result :=  'WM_MDIMAXIMIZE';
    WM_MDITILE: Result :=  'WM_MDITILE';
    WM_MDICASCADE: Result :=  'WM_MDICASCADE';
    WM_MDIICONARRANGE: Result :=  'WM_MDIICONARRANGE';
    WM_MDIGETACTIVE: Result :=  'WM_MDIGETACTIVE';
    WM_MDISETMENU: Result :=  'WM_MDISETMENU';
    WM_ENTERSIZEMOVE: Result :=  'WM_ENTERSIZEMOVE';
    WM_EXITSIZEMOVE: Result :=  'WM_EXITSIZEMOVE';
    WM_DROPFILES: Result :=  'WM_DROPFILES';
    WM_MDIREFRESHMENU: Result :=  'WM_MDIREFRESHMENU';
    WM_MOUSEHOVER: Result :=  'WM_MOUSEHOVER';
    WM_MOUSELEAVE: Result :=  'WM_MOUSELEAVE';
    WM_NCMOUSEHOVER: Result :=  'WM_NCMOUSEHOVER';
    WM_NCMOUSELEAVE: Result :=  'WM_NCMOUSELEAVE';
    WM_WTSSESSION_CHANGE: Result :=  'WM_WTSSESSION_CHANGE';
    WM_TABLET_FIRST: Result :=  'WM_TABLET_FIRST';
    WM_TABLET_LAST: Result :=  'WM_TABLET_LAST';
    WM_CUT: Result :=  'WM_CUT';
    WM_COPY: Result :=  'WM_COPY';
    WM_PASTE: Result :=  'WM_PASTE';
    WM_CLEAR: Result :=  'WM_CLEAR';
    WM_UNDO: Result :=  'WM_UNDO';
    WM_RENDERFORMAT: Result :=  'WM_RENDERFORMAT';
    WM_RENDERALLFORMATS: Result :=  'WM_RENDERALLFORMATS';
    WM_DESTROYCLIPBOARD: Result :=  'WM_DESTROYCLIPBOARD';
    WM_DRAWCLIPBOARD: Result :=  'WM_DRAWCLIPBOARD';
    WM_PAINTCLIPBOARD: Result :=  'WM_PAINTCLIPBOARD';
    WM_VSCROLLCLIPBOARD: Result :=  'WM_VSCROLLCLIPBOARD';
    WM_SIZECLIPBOARD: Result :=  'WM_SIZECLIPBOARD';
    WM_ASKCBFORMATNAME: Result :=  'WM_ASKCBFORMATNAME';
    WM_CHANGECBCHAIN: Result :=  'WM_CHANGECBCHAIN';
    WM_HSCROLLCLIPBOARD: Result :=  'WM_HSCROLLCLIPBOARD';
    WM_QUERYNEWPALETTE: Result :=  'WM_QUERYNEWPALETTE';
    WM_PALETTEISCHANGING: Result :=  'WM_PALETTEISCHANGING';
    WM_PALETTECHANGED: Result :=  'WM_PALETTECHANGED';
    WM_HOTKEY: Result :=  'WM_HOTKEY';
    WM_PRINT: Result :=  'WM_PRINT';
    WM_PRINTCLIENT: Result :=  'WM_PRINTCLIENT';
    WM_APPCOMMAND: Result :=  'WM_APPCOMMAND';
    WM_THEMECHANGED: Result :=  'WM_THEMECHANGED';
    WM_HANDHELDFIRST: Result :=  'WM_HANDHELDFIRST';
    WM_HANDHELDLAST: Result :=  'WM_HANDHELDLAST';
    WM_PENWINFIRST: Result :=  'WM_PENWINFIRST';
    WM_PENWINLAST: Result :=  'WM_PENWINLAST';
    WM_COALESCE_FIRST: Result :=  'WM_COALESCE_FIRST';
    WM_COALESCE_LAST: Result :=  'WM_COALESCE_LAST';
    WM_DDE_INITIATE: Result :=  'WM_DDE_INITIATE';
    WM_DDE_TERMINATE: Result :=  'WM_DDE_TERMINATE';
    WM_DDE_ADVISE: Result :=  'WM_DDE_ADVISE';
    WM_DDE_UNADVISE: Result :=  'WM_DDE_UNADVISE';
    WM_DDE_ACK: Result :=  'WM_DDE_ACK';
    WM_DDE_DATA: Result :=  'WM_DDE_DATA';
    WM_DDE_REQUEST: Result :=  'WM_DDE_REQUEST';
    WM_DDE_POKE: Result :=  'WM_DDE_POKE';
    WM_DDE_EXECUTE: Result :=  'WM_DDE_EXECUTE';
    WM_DWMCOMPOSITIONCHANGED: Result :=  'WM_DWMCOMPOSITIONCHANGED';
    WM_DWMNCRENDERINGCHANGED: Result :=  'WM_DWMNCRENDERINGCHANGED';
    WM_DWMCOLORIZATIONCOLORCHANGED: Result :=  'WM_DWMCOLORIZATIONCOLORCHANGED';
    WM_DWMWINDOWMAXIMIZEDCHANGE: Result :=  'WM_DWMWINDOWMAXIMIZEDCHANGE';
    WM_APP: Result :=  'WM_APP';

    // COMPONENT MESSAGES
    CM_ACTIVATE: Result := 'CM_ACTIVATE';
    CM_DEACTIVATE: Result := 'CM_DEACTIVATE';
    CM_GOTFOCUS: Result := 'CM_GOTFOCUS';
    CM_LOSTFOCUS: Result := 'CM_LOSTFOCUS';
    CM_CANCELMODE: Result := 'CM_CANCELMODE';
    CM_DIALOGKEY: Result := 'CM_DIALOGKEY';
    CM_DIALOGCHAR: Result := 'CM_DIALOGCHAR';
    CM_FOCUSCHANGED: Result := 'CM_FOCUSCHANGED';
    CM_PARENTFONTCHANGED: Result := 'CM_PARENTFONTCHANGED';
    CM_PARENTCOLORCHANGED: Result := 'CM_PARENTCOLORCHANGED';
    CM_HITTEST: Result := 'CM_HITTEST';
    CM_VISIBLECHANGED: Result := 'CM_VISIBLECHANGED';
    CM_ENABLEDCHANGED: Result := 'CM_ENABLEDCHANGED';
    CM_COLORCHANGED: Result := 'CM_COLORCHANGED';
    CM_FONTCHANGED: Result := 'CM_FONTCHANGED';
    CM_CURSORCHANGED: Result := 'CM_CURSORCHANGED';
    CM_CTL3DCHANGED: Result := 'CM_CTL3DCHANGED';
    CM_PARENTCTL3DCHANGED: Result := 'CM_PARENTCTL3DCHANGED';
    CM_TEXTCHANGED: Result := 'CM_TEXTCHANGED';
    CM_MOUSEENTER: Result := 'CM_MOUSEENTER';
    CM_MOUSELEAVE: Result := 'CM_MOUSELEAVE';
    CM_MENUCHANGED: Result := 'CM_MENUCHANGED';
    CM_APPKEYDOWN: Result := 'CM_APPKEYDOWN';
    CM_APPSYSCOMMAND: Result := 'CM_APPSYSCOMMAND';
    CM_BUTTONPRESSED: Result := 'CM_BUTTONPRESSED';
    CM_SHOWINGCHANGED: Result := 'CM_SHOWINGCHANGED';
    CM_ENTER: Result := 'CM_ENTER';
    CM_EXIT: Result := 'CM_EXIT';
    CM_DESIGNHITTEST: Result := 'CM_DESIGNHITTEST';
    CM_ICONCHANGED: Result := 'CM_ICONCHANGED';
    CM_WANTSPECIALKEY: Result := 'CM_WANTSPECIALKEY';
    CM_INVOKEHELP: Result := 'CM_INVOKEHELP';
    CM_WINDOWHOOK: Result := 'CM_WINDOWHOOK';
    CM_RELEASE: Result := 'CM_RELEASE';
    CM_SHOWHINTCHANGED: Result := 'CM_SHOWHINTCHANGED';
    CM_PARENTSHOWHINTCHANGED: Result := 'CM_PARENTSHOWHINTCHANGED';
    CM_SYSCOLORCHANGE: Result := 'CM_SYSCOLORCHANGE';
    CM_WININICHANGE: Result := 'CM_WININICHANGE';
    CM_FONTCHANGE: Result := 'CM_FONTCHANGE';
    CM_TIMECHANGE: Result := 'CM_TIMECHANGE';
    CM_TABSTOPCHANGED: Result := 'CM_TABSTOPCHANGED';
    CM_UIACTIVATE: Result := 'CM_UIACTIVATE';
    CM_UIDEACTIVATE: Result := 'CM_UIDEACTIVATE';
    CM_DOCWINDOWACTIVATE: Result := 'CM_DOCWINDOWACTIVATE';
    CM_CONTROLLISTCHANGE: Result := 'CM_CONTROLLISTCHANGE';
    CM_GETDATALINK: Result := 'CM_GETDATALINK';
    CM_CHILDKEY: Result := 'CM_CHILDKEY';
    CM_DRAG: Result := 'CM_DRAG';
    CM_HINTSHOW: Result := 'CM_HINTSHOW';
    CM_DIALOGHANDLE: Result := 'CM_DIALOGHANDLE';
    CM_ISTOOLCONTROL: Result := 'CM_ISTOOLCONTROL';
    CM_RECREATEWND: Result := 'CM_RECREATEWND';
    CM_INVALIDATE: Result := 'CM_INVALIDATE';
    CM_SYSFONTCHANGED: Result := 'CM_SYSFONTCHANGED';
    CM_CONTROLCHANGE: Result := 'CM_CONTROLCHANGE';
    CM_CHANGED: Result := 'CM_CHANGED';
    CM_DOCKCLIENT: Result := 'CM_DOCKCLIENT';
    CM_UNDOCKCLIENT: Result := 'CM_UNDOCKCLIENT';
    CM_FLOAT: Result := 'CM_FLOAT';
    CM_BORDERCHANGED: Result := 'CM_BORDERCHANGED';
    CM_BIDIMODECHANGED: Result := 'CM_BIDIMODECHANGED';
    CM_PARENTBIDIMODECHANGED: Result := 'CM_PARENTBIDIMODECHANGED';
    CM_ALLCHILDRENFLIPPED: Result := 'CM_ALLCHILDRENFLIPPED';
    CM_ACTIONUPDATE: Result := 'CM_ACTIONUPDATE';
    CM_ACTIONEXECUTE: Result := 'CM_ACTIONEXECUTE';
    CM_HINTSHOWPAUSE: Result := 'CM_HINTSHOWPAUSE';
    CM_DOCKNOTIFICATION: Result := 'CM_DOCKNOTIFICATION';
    CM_MOUSEWHEEL: Result := 'CM_MOUSEWHEEL';
    CM_ISSHORTCUT: Result := 'CM_ISSHORTCUT';
    CM_INVALIDATEDOCKHOST: Result := 'CM_INVALIDATEDOCKHOST';
    CM_SETACTIVECONTROL: Result := 'CM_SETACTIVECONTROL';
    CM_POPUPHWNDDESTROY: Result := 'CM_POPUPHWNDDESTROY';
    CM_CREATEPOPUP: Result := 'CM_CREATEPOPUP';
    CM_DESTROYHANDLE: Result := 'CM_DESTROYHANDLE';
    CM_MOUSEACTIVATE: Result := 'CM_MOUSEACTIVATE';
    CM_CONTROLLISTCHANGING: Result := 'CM_CONTROLLISTCHANGING';
    CM_BUFFEREDPRINTCLIENT: Result := 'CM_BUFFEREDPRINTCLIENT';
    CM_UNTHEMECONTROL: Result := 'CM_UNTHEMECONTROL';

    else
      Result := PChar('$' + IntToHex(m.Msg, 4) + '  (d' + IntToStr(m.Msg) + ')');
  end;

  Result := Result + ' W:' + IntToStr(m.WParam) + ' L:' + IntToStr(m.LParam) + ' R:' + IntToStr(m.Result);
end;

procedure TMessageTracer.HookWndProc(var Message: TMessage);
begin
  fOriginalWndProc(Message);

  if Assigned(fIgnores) then // object has not destroyed itself
    if fIgnores.IndexOf(Pointer(Message.Msg)) < 0 then
      Trace(GetMessageInfoString(Message));
end;

procedure TMessageTracer.Ignore(const aMsgs: array of Integer);
var
  m: Integer;
begin
  for m in aMsgs do
    if fIgnores.IndexOf(Pointer(m)) < 0 then
      fIgnores.Add(Pointer(m));
end;

procedure TMessageTracer.Start;
begin
  Catch(fTarget);
end;

procedure TMessageTracer.Stop;
begin
  UnCatch(fTarget);
end;

procedure TMessageTracer.DoTrace(const aMessage: string);
begin
end;

procedure TMessageTracer.Trace(const aMessage: string);
begin
  if Assigned(OnTrace) then
    OnTrace(aMessage);
  DoTrace(aMessage);
end;

{ TDebugMessageTracer }

procedure TDebugMessageTracer.DoTrace(const aMessage: string);
begin
  inherited;
  OutputDebugString(PChar(aMessage));
end;

{ TConsoleMessageTracer }

destructor TConsoleMessageTracer.Destroy;
begin
  FreeConsole();
  inherited;
end;

procedure TConsoleMessageTracer.DoCatch(const aTarget: TControl);
begin
  inherited;
  Initialize();
end;

procedure TConsoleMessageTracer.DoTrace(const aMessage: string);
begin
  inherited;
  WriteLn(aMessage);
end;

class procedure TConsoleMessageTracer.Initialize;
begin
  if not Initialized then
  begin
    Initialized := True;

    if not AllocConsole() then
      raise Exception.Create('Couldn''t allocate console');
  end;
end;

end.
